<?php
def("amicitia/arquitectura/","Cabeceador");
def("amicitia/arquitectura/","Sesion");
def("amicitia/arquitectura/errores/","Error");

/**
 * Clase encargada de manejar los errores y redireccionarlos a una 'Accion'
 * correspondiente.
 * 
 * @author Willy A. Kuster
 */
class Recuperador {
	
	const OCURRIDO = "OCURRIDO";
	const ULTIMA_EXCEPCION = "ULTIMA_EXCEPCION";
	const ULTIMO_ERROR = "ULTIMO_ERROR";
	const ULTIMO_ERROR_FATAL = "ULTIMO_ERROR_FATAL";
	const EXCEPCION = 0;
	const ERROR = 1;
	const ERROR_FATAL = 2;
	const ACTIVADO = 1;
	const DESACTIVADO = 0;
	const TAG_ERRORFATAL = "<#errorfatal>";
	const TAG_LATAFRORRE = "<latafrorre#>";
	private static $instancia;
	private $estado = self::DESACTIVADO;
	private $errorInesperado;
	private $errores;
	
	public static function getRecuperador(){
		if (!isset(self::$instancia)) {
			self::$instancia = new Recuperador();
		}
		return self::$instancia;
	}
	
	private function __construct() {
		$this->errorInesperado = conf("errores","mapeos","error_inesperado");
		$this->errores = conf("errores","mapeos");
		if(conf("errores","manejo_errores")){
			$this->activar();
		}
	}
	
	public function recuperarErrorFatal($mensaje){
		if($this->estado == self::DESACTIVADO || !$this->verificarAccionMetodo($this->errorInesperado))
			return false;
		
		$_SESSION[self::ULTIMO_ERROR_FATAL] = $mensaje;
		$accion_url = $this->errorInesperado["accion_url"];
		$metodo = $this->errorInesperado["metodo"];
		if (ini_get('log_errors'))
        	error_log($mensaje);
        $_SESSION[self::OCURRIDO] = self::ERROR_FATAL;
		Cabeceador::redireccionarAccion($accion_url,$metodo);
	}
	
	public function recuperarError($errno, $errstr, $errfile, $errline){
		if($this->estado == self::DESACTIVADO || !$this->verificarAccionMetodo($this->errorInesperado))
			return false;
		
		$error = new Error($errno, $errstr, $errfile, $errline);
		Sesion::getSesion()->guardarObjeto(self::ULTIMO_ERROR,$error);
		$accion_url = $this->errorInesperado["accion_url"];
		$metodo = $this->errorInesperado["metodo"];
		if (ini_get('log_errors'))
        	error_log(sprintf($error));
        $_SESSION[self::OCURRIDO] = self::ERROR;
		Cabeceador::redireccionarAccion($accion_url,$metodo);
	}
	
	public function recuperarExcepcion(Exception $excepcion){
		if($this->estado == self::DESACTIVADO)
			throw $excepcion;
		
		$arregloErrores = $this->errores;
		if(isset($arregloErrores[get_class($excepcion)])) {
			if(!$this->verificarAccionMetodo($arregloErrores[get_class($excepcion)])){
				$this->desactivar();
				throw $excepcion;
			}				
			$accion_url = $arregloErrores[get_class($excepcion)]["accion_url"];
			$metodo = $arregloErrores[get_class($excepcion)]["metodo"];
		} else {
			if(!$this->verificarAccionMetodo($this->errorInesperado)){
				$this->desactivar();
				throw $excepcion;
			}				
			$accion_url = $this->errorInesperado["accion_url"];
			$metodo = $this->errorInesperado["metodo"];
		}
		Sesion::getSesion()->guardarObjeto(self::ULTIMA_EXCEPCION,$excepcion);
		$_SESSION[self::OCURRIDO] = self::EXCEPCION;
		Cabeceador::redireccionarAccion($accion_url,$metodo);
	}
	
	private function activar() {
		if(!$this->verificarAccionMetodo($this->errorInesperado) || !conf("errores","manejo_errores"))
			return false;
			
		set_error_handler("errorHandler");
		
		$accion_url = $this->errorInesperado["accion_url"];
		$metodo = $this->errorInesperado["metodo"];
		// Parse,Compile, Core, etc... Errors
		ob_start("outputCapture");
		ini_set('html_errors',false);
		ini_set('error_prepend_string',self::TAG_ERRORFATAL);//'<head><META http-equiv="refresh" content="0;URL='.$accion_url.'/'.$metodo.'?msj='
		ini_set('error_append_string',self::TAG_LATAFRORRE);//'"></head></html>'
		
		$this->estado = self::ACTIVADO;
		
		return true;
	}
	
	private function desactivar() {
		restore_error_handler();
		
		ini_set('html_errors',true);
		ini_set('error_prepend_string','');
		ini_set('error_append_string','');
		
		$this->estado = self::DESACTIVADO;
	}
	
	public function getEstado() {
		return $this->estado;
	}
	
	private function verificarAccionMetodo($accionMetodo) {
		$accionMatch = preg_match("/[^A-Za-z0-9_]/",$accionMetodo["accion_url"]);
		$metodoMatch = preg_match("/[^A-Za-z0-9_]/",$accionMetodo["metodo"]);
		return ($accionMatch == 0 && $metodoMatch == 0 && $accionMetodo["accion_url"]!="");
	}
	
	public function verificarMapeoError($mapeoAccion){
		$arregloErroresAsoc = $this->errores;
		$arregloErroresNum = array_values($arregloErroresAsoc);
		for($i=0;$i<count($arregloErroresNum);$i++){
			if($mapeoAccion == $arregloErroresNum[$i]["accion_url"]){
				$this->desactivar();
				return;
			}
		}
	}
	
	public function detectarErrorEnBuffer($buffer){
		if($this->estado == self::DESACTIVADO || !conf("errores","manejo_errores"))
			return $buffer;
		
		$ini = strpos($buffer,self::TAG_ERRORFATAL);
		if ($ini !== false) {
			$fin = strpos($buffer,self::TAG_LATAFRORRE);
			if ($fin !== false && $ini < $fin) {
				$ini = $ini+strlen(self::TAG_ERRORFATAL)+1;
				$fin = ($fin-$ini)-1;
				$mensaje = substr($buffer,$ini,$fin);
				$this->recuperarErrorFatal($mensaje);
			}
		}
		
		return $buffer;
	}
	
	public static function getOcurrido() {
		if(isset($_SESSION[self::OCURRIDO])){
			$ocurrido = $_SESSION[self::OCURRIDO];
			unset($_SESSION[self::OCURRIDO]);
			return $ocurrido;
		}
	}
	
	public static function getUltimoError() {
		if(Sesion::getSesion()->existeObjeto(Recuperador::ULTIMO_ERROR)){
			$error = Sesion::getSesion()->recobrarObjeto(Recuperador::ULTIMO_ERROR);
			Sesion::getSesion()->eliminarObjeto(Recuperador::ULTIMO_ERROR);
			return $error;
		}
	}
	
	public static function getUltimaExcepcion() {
		if(Sesion::getSesion()->existeObjeto(Recuperador::ULTIMA_EXCEPCION)){
			$excepcion = Sesion::getSesion()->recobrarObjeto(Recuperador::ULTIMA_EXCEPCION);
			Sesion::getSesion()->eliminarObjeto(Recuperador::ULTIMA_EXCEPCION);
			return $excepcion;
		}
	}
	
	public static function getUltimoErrorFatal() {
		if(isset($_SESSION[Recuperador::ULTIMO_ERROR_FATAL])){
			$mensaje = $_SESSION[Recuperador::ULTIMO_ERROR_FATAL];
			unset($_SESSION[Recuperador::ULTIMO_ERROR_FATAL]);
			return $mensaje;
		}
	}
	
}

function errorHandler($errno, $errstr, $errfile, $errline) {
	return Recuperador::getRecuperador()->recuperarError($errno, $errstr, $errfile, $errline);
}

function outputCapture($buffer){
	return Recuperador::getRecuperador()->detectarErrorEnBuffer($buffer);
}

?>